/*	
	Copyright (C) 2014  Ilchenko Vladuslav Oleksiyovich

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or any
	later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "RSA.h"

string RSA::DecodeString(RSAKey key,ZZ cript)
{
	ZZ temp__;
	string temp_;
	temp__ = cript;
	Decode(temp__,to_ZZ(key.d.c_str()),to_ZZ(key.n.c_str()));
	ZZToStr(temp__,temp_);
	return temp_;
}

ZZ RSA::EncodeString(RSAKey key,string text)
{
	ZZ tem__ZZ;
	StrToZZ(text,tem__ZZ);
	Encode(tem__ZZ,to_ZZ(key.e.c_str()),to_ZZ(key.n.c_str()));
	return tem__ZZ;
}

inline bool RSA::is_base64(unsigned char c)
{
	return (isalnum(c) || (c == '+') || (c == '/'));
}

void RSA::Decode(ZZ &to_dec,ZZ d,ZZ n)
{
	to_dec = PowerMod(to_dec,d,n);
}

void RSA::Encode(ZZ &to_enc,ZZ e,ZZ n)
{
	to_enc = PowerMod(to_enc,e,n);
}

void RSA::ZZToStr(ZZ &LP_Num,string &LP_Txt)
{
	strstream LP_num_tmp;
	string str,buf;
	LP_num_tmp << LP_Num;
	LP_num_tmp >> str;
	for(register int i = 0;(size_t)i<str.length()-2;i+=3)
	{
		buf=str[i];buf+=str[i+1];buf+=str[i+2];
		LP_Txt += atoi(buf.c_str())/4;
	}
}

void RSA::StrToZZ(string &LP_txt,ZZ &LP_Num)
{
	string tmp;
	char tmp_[5];
	for(register int i = 0;(size_t)i < LP_txt.length(); ++i)
	{
		itoa((int)LP_txt[i] * 4,tmp_,10);
		tmp += tmp_;
	}
	LP_Num = to_ZZ(tmp.c_str());
}

string RSA::Base64_To_Str(string b64)
{
	
	int in_ = 0;
	size_t i = 0,j = 0;
	unsigned char char_array_4[4], char_array_3[3];
	string ret;
	for(size_t in_len = b64.size();in_len != 0  && ( b64[in_] != '=') && is_base64(b64[in_]);--in_len)
	{
		char_array_4[i++] = b64[in_++];
		if (i ==4) 
		{
			for (i = 0; i <4; i++)
			char_array_4[i] = static_cast<unsigned char>(base64_chars.find(char_array_4[i]));

			char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
			char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
			char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

			for (i = 0; (i < 3); i++)
				ret += char_array_3[i];
			i = 0;
		}
	}
		if (i!=0)
		{
			for (j = i; j <4; j++)
				char_array_4[j] = 0;

			for (j = 0; j <4; j++)
				char_array_4[j] = static_cast<unsigned char>(base64_chars.find(char_array_4[j]));

			char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
			char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
			char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

			for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
		}
	return ret;
}

std::string RSA::Str_To_Base64(string str)
{
	std::string ret;
	unsigned char const *bytes_to_encode = (unsigned char const *)str.c_str();
	size_t i = 0,j = 0, in_len;
	unsigned char char_array_3[3],char_array_4[4];
	for(in_len = str.length();in_len != 0;in_len--)
	{
		char_array_3[i++] = *(bytes_to_encode++);
		if (i != 0) 
		{
			if(in_len-1 == 0) 
				for(j = i; j < 3; j++) char_array_3[j] = '\0';
			if(i == 3 || in_len-1 == 0){
			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
			char_array_4[3] = char_array_3[2] & 0x3f;}
			if(i == 3&&in_len-1 != 0) 
			{for(i = 0; i < 4 ; i++) ret += base64_chars[char_array_4[i]]; i = 0;}
			if(in_len-1 == 0)
			{
				for (j = 0; j < i + 1; j++) ret += base64_chars[char_array_4[j]];
				while(i++ < 3) ret += '=';
			}
		}
	}
	return ret;
}

void RSA::Dec_To_Hex(ZZ dec,string &str)
{
	register const char hexchar[17] = "0123456789abcdef";
	for (; dec > 0; dec /= 16) str += hexchar[to_int(dec % 16)];
	reverse(str.begin(), str.end());
}

void RSA::Hex_To_Dec(string &str,ZZ &dec)
{
	dec = 0;
	for(register int i = 0;(size_t)i < str.length();++i)
	{
		str[i] -= 48 + ((str[i] - 48) > 16 ? 39 : 0);
		dec = (dec + str[i]) * (i == str.length()-1 ? 1 : 16);
	}
}

RSA::RSA()
{
	base64_chars = 
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz"
            "0123456789+/";
}

RSA::~RSA()
{
}

int RSA::RSAKeyGen(RSAKey &key,long lbits)
{
	steady_clock::time_point p1 = steady_clock::now();
	ZZ q, p, n,x,e,d; //
	SetSeed(to_ZZ((unsigned)time(NULL)));

	q = RandomPrime_ZZ(lbits/2);
	p = RandomPrime_ZZ(lbits/2);
	
	n = q * p;
	
	x=(q-1)*(p-1);

	do e = RandomBnd(x); while(GCD(e,x)!=1);
	d = InvMod(e,x);

	strstream stdr,ster,stnr;
	stdr<<d;
	stdr>>key.d;
	ster<<e;
	ster>>key.e;
	stnr<<n;
	stnr>>key.n;

	steady_clock::time_point p2 = steady_clock::now();
	duration<double> span = duration_cast<duration<double>>(p2 - p1);
	cout<<"Counted for "<<span.count()<<" seconds."<<endl;
	return 0;
}